package XMFramework.Core
{		
	import XMFramework.Core.Internal.SResourceLoader;
	import XMFramework.Interface.IResources;
	import XMFramework.Interface.IResourcesManager;
	
	import flash.events.Event;

	internal class SResourceManager implements IResourcesManager
	{
		public static const RESOURCE_IMMEDIATELY : String		=	"immediate";
		public static const RESOURCE_AFTERLOAD : String			=	"afterload";
		public static const RESOURCE_OM_DEMAND : String			=	"demand";
		
		// ResourceLoader data list
		private var resourceData : Object		=	new Object();
		private var remoteAssetData : Object			=	new Object();
		
		private var stepLoadFunc : Function		=	null;
		private var completeFunc : Function		=	null;
		private var numImmResource : uint		=	0;
		private var loadedResource : uint		=	0;
		
		internal var isInitCompleted:Boolean	=	false;
		
		// Call to load all resource data to memory 
		internal function startLoadResources( mediaPath:String, materials:Object, patchfiles:Object, requireAssets:Array, onStepLoad:Function, onComplete:Function ) : void
		{
			stepLoadFunc	=	onStepLoad;
			completeFunc	=	onComplete;
			
			numImmResource	=	0;
			var i:String, j:String;
			
			// Count number of resource type load immediately and require asset must download at first time loading
			for( i in materials )
			{
				if( String(materials[i].priority).toLowerCase() == RESOURCE_IMMEDIATELY )
					numImmResource++;
				
				else for( j in requireAssets )
				{
					if( materials[i].bucket == requireAssets[j] )
					{
						numImmResource++;
						break;
					}
				}
			}			
				
			// Init asset database and force download for require list
			for( i in materials )
			{				
				var resName : String			=	materials[i].bucket;
				var resPriority : String		=	materials[i].priority;
				
				// Set default url
				var resURL : String				=	mediaPath + materials[i].url;
				
				// Check patch file mode: patchfiles != null
				for( j in patchfiles )
				{
					if( patchfiles[j].bucket == resName )
					{
						resURL					=	patchfiles[j].base_url + materials[i].url;
						break;
					}
				}
				
				var loader : SResourceLoader	=	new SResourceLoader( resName, resURL, resPriority );
				loader.onLoadResultFunc			=	onResLoadResult;
				resourceData[resName]			=	loader;
				
				if( resPriority.toLowerCase() == RESOURCE_IMMEDIATELY )
					loader.loadResource();
				
				// Exist in require list
				else for( j in requireAssets )
				{
					if( resName == requireAssets[j] )
					{
						loader.loadResource();
						break;
					}
				}
			}
		}
		
		internal function calResourcesTotalByte():int
		{
			var totalByte:int	=	0;
			for( var i:String in resourceData )
				if( resourceData[i].resourcePriority.toLowerCase() == RESOURCE_IMMEDIATELY )
					totalByte	+=	resourceData[i].resourceTotalByte;
			
			if( totalByte == 0 )
				return int.MAX_VALUE;
			
			return totalByte;
		}
		
		internal function calResourcesLoadedByte():int
		{
			var loadedByte:int	=	0;
			for( var i:String in resourceData )
				if( resourceData[i].resourcePriority.toLowerCase() == RESOURCE_IMMEDIATELY )
					loadedByte	+=	resourceData[i].resourceLoadedByte;
			
			return loadedByte;
		}
		
		private function onResLoadResult( success : Boolean, downloadComplete:Boolean, resLoader : SResourceLoader, bucket : String, totalByte:uint, loadedByte:uint, error : * = null ) : void
		{
			if( success )
			{
				if( stepLoadFunc != null )
					stepLoadFunc.call( null, bucket, totalByte, loadedByte );
				
				if( downloadComplete )
				{
					loadedResource	+=	1;
					resLoader.onLoadResultFunc	=	null;
					
					if( loadedResource >= numImmResource )
					{
						isInitCompleted	=	true;
						
						// Finish download immediate resource
						if( completeFunc != null )
						{
							// Tell engine we done
							completeFunc.call();
							
							// Now start dowloading afterload type resource
							for( var i:String in resourceData )
							{	
								var loader : SResourceLoader	=	resourceData[i] as SResourceLoader;								
								if( loader.resourcePriority.toLowerCase() == RESOURCE_AFTERLOAD )
									loader.loadResource();
							}
						}
						
						// Release pointer
						stepLoadFunc	=	null;
						completeFunc	=	null;
					}
				}
			}
			else
				throw new SError( SError.ERR_RES_LOAD_FAILED, error, RESOURCE_IMMEDIATELY, bucket );
		}
		
		////////////////////////////////////////////////////////
		// Functional methods
		////////////////////////////////////////////////////////
		
		/**
		 * Transfer resource class to IResources object or start load process if not exist
		 * @param bundle Name of resource bundle
		 * @param className Class name of request resource
		 * @param obj Callback object when resource is ready
		 * 
		 */		
		public function getResource( bucket : String, className : String, obj : IResources ) : void
		{
			if( resourceData.hasOwnProperty( bucket ) )
				resourceData[bucket].getClass( className, obj );
			else
				trace( "ResourceManager::Get resource class not found for bucket: " + bucket );
		}
		
		public function clearResourceRequest( obj : IResources ) : void
		{
			for each( var resLoader : SResourceLoader in resourceData )
				resLoader.clearResourceRequest( obj );
		}
			
		public function getResourceFunc( bucket : String, className : String, func : Function, thisObject : * = null ) : void
		{
			if( resourceData.hasOwnProperty( bucket ) )
				resourceData[bucket].getClassFunc( className, func, thisObject );
			else
				trace( "ResourceManager::Get resource class not found for bucket: " + bucket );
		}
		
		public function getImmediateResourceFunc( bucket : String, className : String ) : Class
		{
			if( resourceData.hasOwnProperty( bucket ) )
				return resourceData[bucket].getImmediateClassFunc( className );
			else
				trace( "ResourceManager::Get resource class not found for bucket: " + bucket + " " + className );
			return XFLoadingIcon;
		}
		
		public function getResourceFuncEx( bucket : String, className : String, func : Function, thisObject : *, ...params ) : void
		{
			if( resourceData.hasOwnProperty( bucket ) )
				resourceData[bucket].getClassFuncEx.apply( null, [className, func, thisObject].concat( params ) );
			else
				trace( "ResourceManager::Get resource class not found for bucket: " + bucket );
		}
		
		public function getRemoteAssetFunc( url : String, func : Function, thisObject : * = null ) : void
		{
			if( ! remoteAssetData.hasOwnProperty( url ) )
				remoteAssetData[url]	=	new SImageLoader( url );
			
			remoteAssetData[url].getImage( func, thisObject );
		}
		
		public function backgroundLoading():void
		{
			for( var i:String in resourceData )
			{
				var loader:SResourceLoader	=	resourceData[i] as SResourceLoader;
				if( loader.isLoaded == false && loader.resourcePriority.toLowerCase() == RESOURCE_AFTERLOAD )
				{
					loader.addEventListener( SResourceLoader.LOAD_COMPLETED, onResourceLoaded );
					loader.loadResource();
					break;
				}
			}
		}
	
		private function onResourceLoaded( e:Event ):void
		{
			var loader:SResourceLoader	=	e.target as SResourceLoader;
			loader.removeEventListener( SResourceLoader.LOAD_COMPLETED, onResourceLoaded );
			
			// Download next resource
			backgroundLoading();
		}
	}
}